package com.ingenico.insider.nodes;

import java.awt.Paint;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.logging.Logger;

import com.ingenico.insider.services.impl.CanvasSupplier;
import com.ingenico.insider.services.impl.CurvesSupplier;
import com.ingenico.tools.data.chopping.Chopper;
import com.ingenico.tools.data.sampling.PathSampler;
import com.ingenico.tools.nio.channel.RandomReadableSampleChannel;

import edu.umd.cs.piccolo.PCamera;
import edu.umd.cs.piccolo.PNode;

public class PSampleChannel extends PNode {
	private static final Logger _logger = Logger.getLogger(PSampleChannel.class.getCanonicalName());

	/**
	 * Generated Serial Version UID
	 */
	private static final long serialVersionUID = -2923052585637966767L;

	/** 
	 * The property name that identifies a change of this node's stroke paint
	 * (see {@link #getStrokePaint getStrokePaint}). Both old and new value will
	 * be set correctly to Paint objects in any property change event.
	 */
	public static final String PROPERTY_STROKE_PAINT = "strokePaint";
	public static final int PROPERTY_CODE_STROKE_PAINT = 1 << 16;

	public static final String FILE_KEY = "file";

	private Paint strokePaint;

	private RandomReadableSampleChannel dataChannel;

	public RandomReadableSampleChannel getDataChannel() {
		return dataChannel;
	}

	public void setDataChannel(RandomReadableSampleChannel dataChannel) throws IOException {
		this.dataChannel = dataChannel;
		// The curves are split into chunks and only partially available on the canvas
		// We only set the horizontal size to make sure that space will be allocated even if some
		// chunks have not been processed yet... curve height does not really matters here...
		setBounds(0, 0, (double)dataChannel.size(), 1);
		setPickable(false);
	}

	public Paint getStrokePaint() {
		return strokePaint;
	}

	public void setStrokePaint(final Paint aPaint) {
		Paint old = strokePaint;
		strokePaint = aPaint;
		Iterator<PNode> children = getChildrenIterator();
		while (children.hasNext()) {
			PNode child = children.next();
			if (child instanceof PCurve) {
				((PCurve)child).setStrokePaint(aPaint);
			}
		}

		invalidatePaint();
		firePropertyChange(PROPERTY_CODE_STROKE_PAINT ,PROPERTY_STROKE_PAINT, old, strokePaint);
	}

	@Override
	public void setTransparency(float zeroToOne) {
		Iterator<PNode> children = getChildrenIterator();
		while (children.hasNext()) {
			PNode child = children.next();
			if (child instanceof PCurve) {
				((PCurve)child).setTransparency(zeroToOne);
			}
		}

		super.setTransparency(zeroToOne);
	}

	public PCurve getChild(final Class<?> samplerClass) {
		ListIterator<PCurve> it = getChildrenIterator();
		while (it.hasNext()) {
			PCurve curve = (PCurve) it.next();

			if (curve.getPathSampler().getClass() == samplerClass) {
				return curve;
			}
		}
		return null;
	}

	public void addChild(final PathSampler sampler, final Chopper chopper) {
		if (getChild(sampler.getClass()) == null) {
			final PThreadedChoppingNode childCurve = new PThreadedChoppingNode(dataChannel);
	
			childCurve.setChopper(chopper);
			childCurve.setPathSampler(sampler);
			childCurve.setStrokePaint(strokePaint);
	
			_logger.fine("Registering node from camera observer : " + childCurve);
			CurvesSupplier.getInstance().getCameraObserver().addGeometryChangeListener(childCurve);
	
			final PCamera camera = CanvasSupplier.getInstance().getCanvas().getCamera();
			super.addChild(childCurve);
			childCurve.initActivity(camera.getViewBounds(), camera.getBounds(), camera.getViewTransform());
		} else {
			_logger.warning(sampler.getClass().getCanonicalName() + " is already set. Nothing was done...");
		}
	}

	@Override
	public void removeAllChildren() {
		while (getChildrenCount() > 0) {
			removeChild(0);
		}
	}

	@Override
	public PNode removeChild(int index) {
		final PNode child = super.removeChild(index); 
		if ((child != null) && (child instanceof PCurve)) {
			_logger.fine("Unregistering node from camera observer : " + child);
			CurvesSupplier.getInstance().getCameraObserver().removeGeometryChangeListener((PCurve)child);
		}
		return child;
	}
	
	@Override
	public PNode removeChild(final PNode child) {
		if ((super.removeChild(child) != null) && (child instanceof PCurve)) {
			_logger.fine("Unregistering node from camera observer : " + child);
			CurvesSupplier.getInstance().getCameraObserver().removeGeometryChangeListener((PCurve)child);
		}
		return child;
	}

	@Override
	public String toString() {
		final File file = (File) getAttribute(FILE_KEY);

		String filename;
		if (file != null) {
			filename = file.getName();
		} else {
			filename = "<unknown name>";
		}
		return filename;
	}
}
